1 /*
2 * Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package sun.print;
27
28 import java.lang.ref.SoftReference;
29 import java.util.Hashtable;
30 import sun.font.CharToGlyphMapper;
31 import sun.font.CompositeFont;
32 import sun.font.Font2D;
33 import sun.font.Font2DHandle;
34 import sun.font.FontManager;
35 import sun.font.FontManagerFactory;
36 import sun.font.FontUtilities;
37
38 import java.awt.Color;
39 import java.awt.Font;
40 import java.awt.Graphics2D;
41 import java.awt.Image;
42 import java.awt.Paint;
43 import java.awt.Polygon;
44 import java.awt.Shape;
45
46 import java.text.AttributedCharacterIterator;
47
48 import java.awt.font.FontRenderContext;
49 import java.awt.font.GlyphVector;
50 import java.awt.font.TextAttribute;
51 import java.awt.font.TextLayout;
52
53 import java.awt.geom.AffineTransform;
54 import java.awt.geom.Arc2D;
55 import java.awt.geom.Ellipse2D;
56 import java.awt.geom.Line2D;
57 import java.awt.geom.Point2D;
58 import java.awt.geom.Rectangle2D;
59 import java.awt.geom.RoundRectangle2D;
60 import java.awt.geom.PathIterator;
61
62 import java.awt.image.BufferedImage;
63 import java.awt.image.BufferedImageOp;
64 import java.awt.image.ColorModel;
65 import java.awt.image.DataBuffer;
66 import java.awt.image.DataBufferInt;
67 import java.awt.image.ImageObserver;
68 import java.awt.image.IndexColorModel;
69 import java.awt.image.Raster;
70 import java.awt.image.RenderedImage;
71 import java.awt.image.SampleModel;
72 import java.awt.image.SinglePixelPackedSampleModel;
73 import java.awt.image.VolatileImage;
74 import sun.awt.image.ByteComponentRaster;
75 import sun.awt.image.ToolkitImage;
76 import sun.awt.image.SunWritableRaster;
77
78 import java.awt.print.PageFormat;
79 import java.awt.print.Printable;
80 import java.awt.print.PrinterException;
81 import java.awt.print.PrinterGraphics;
82 import java.awt.print.PrinterJob;
83
84 import java.util.Map;
85
86 public abstract class PathGraphics extends ProxyGraphics2D {
87
88 private Printable mPainter;
89 private PageFormat mPageFormat;
90 private int mPageIndex;
91 private boolean mCanRedraw;
92 protected boolean printingGlyphVector;
93
94 protected PathGraphics(Graphics2D graphics, PrinterJob printerJob,
95 Printable painter, PageFormat pageFormat,
96 int pageIndex, boolean canRedraw) {
97 super(graphics, printerJob);
98
99 mPainter = painter;
100 mPageFormat = pageFormat;
101 mPageIndex = pageIndex;
102 mCanRedraw = canRedraw;
103 }
104
105 /**
106 * Return the Printable instance responsible for drawing
107 * into this Graphics.
108 */
109 protected Printable getPrintable() {
110 return mPainter;
111 }
112
113 /**
114 * Return the PageFormat associated with this page of
115 * Graphics.
116 */
117 protected PageFormat getPageFormat() {
118 return mPageFormat;
119 }
120
121 /**
122 * Return the page index associated with this Graphics.
123 */
124 protected int getPageIndex() {
125 return mPageIndex;
126 }
127
128 /**
129 * Return true if we are allowed to ask the application
130 * to redraw portions of the page. In general, with the
131 * PrinterJob API, the application can be asked to do a
132 * redraw. When PrinterJob is emulating PrintJob then we
133 * can not.
134 */
135 public boolean canDoRedraws() {
136 return mCanRedraw;
137 }
138
139 /**
140 * Redraw a rectanglular area using a proxy graphics
141 */
142 public abstract void redrawRegion(Rectangle2D region,
143 double scaleX, double scaleY,
144 Shape clip,
145 AffineTransform devTransform)
146
147 throws PrinterException ;
148
149 /**
150 * Draws a line, using the current color, between the points
151 * <code>(x1, y1)</code> and <code>(x2, y2)</code>
152 * in this graphics context's coordinate system.
153 * @param x1 the first point's <i>x</i> coordinate.
154 * @param y1 the first point's <i>y</i> coordinate.
155 * @param x2 the second point's <i>x</i> coordinate.
156 * @param y2 the second point's <i>y</i> coordinate.
157 */
158 public void drawLine(int x1, int y1, int x2, int y2) {
159
160 Paint paint = getPaint();
161
162 try {
163 AffineTransform deviceTransform = getTransform();
164 if (getClip() != null) {
165 deviceClip(getClip().getPathIterator(deviceTransform));
166 }
167
168 deviceDrawLine(x1, y1, x2, y2, (Color) paint);
169
170 } catch (ClassCastException e) {
171 throw new IllegalArgumentException("Expected a Color instance");
172 }
173 }
174
175
176 /**
177 * Draws the outline of the specified rectangle.
178 * The left and right edges of the rectangle are at
179 * <code>x</code> and <code>x + width</code>.
180 * The top and bottom edges are at
181 * <code>y</code> and <code>y + height</code>.
182 * The rectangle is drawn using the graphics context's current color.
183 * @param x the <i>x</i> coordinate
184 * of the rectangle to be drawn.
185 * @param y the <i>y</i> coordinate
186 * of the rectangle to be drawn.
187 * @param width the width of the rectangle to be drawn.
188 * @param height the height of the rectangle to be drawn.
189 * @see java.awt.Graphics#fillRect
190 * @see java.awt.Graphics#clearRect
191 */
192 public void drawRect(int x, int y, int width, int height) {
193
194 Paint paint = getPaint();
195
196 try {
197 AffineTransform deviceTransform = getTransform();
198 if (getClip() != null) {
199 deviceClip(getClip().getPathIterator(deviceTransform));
200 }
201
202 deviceFrameRect(x, y, width, height, (Color) paint);
203
204 } catch (ClassCastException e) {
205 throw new IllegalArgumentException("Expected a Color instance");
206 }
207
208 }
209
210 /**
211 * Fills the specified rectangle.
212 * The left and right edges of the rectangle are at
213 * <code>x</code> and <code>x + width - 1</code>.
214 * The top and bottom edges are at
215 * <code>y</code> and <code>y + height - 1</code>.
216 * The resulting rectangle covers an area
217 * <code>width</code> pixels wide by
218 * <code>height</code> pixels tall.
219 * The rectangle is filled using the graphics context's current color.
220 * @param x the <i>x</i> coordinate
221 * of the rectangle to be filled.
222 * @param y the <i>y</i> coordinate
223 * of the rectangle to be filled.
224 * @param width the width of the rectangle to be filled.
225 * @param height the height of the rectangle to be filled.
226 * @see java.awt.Graphics#clearRect
227 * @see java.awt.Graphics#drawRect
228 */
229 public void fillRect(int x, int y, int width, int height){
230
231 Paint paint = getPaint();
232
233 try {
234 AffineTransform deviceTransform = getTransform();
235 if (getClip() != null) {
236 deviceClip(getClip().getPathIterator(deviceTransform));
237 }
238
239 deviceFillRect(x, y, width, height, (Color) paint);
240
241 } catch (ClassCastException e) {
242 throw new IllegalArgumentException("Expected a Color instance");
243 }
244 }
245
246 /**
247 * Clears the specified rectangle by filling it with the background
248 * color of the current drawing surface. This operation does not
249 * use the current paint mode.
250 * <p>
251 * Beginning with Java 1.1, the background color
252 * of offscreen images may be system dependent. Applications should
253 * use <code>setColor</code> followed by <code>fillRect</code> to
254 * ensure that an offscreen image is cleared to a specific color.
255 * @param x the <i>x</i> coordinate of the rectangle to clear.
256 * @param y the <i>y</i> coordinate of the rectangle to clear.
257 * @param width the width of the rectangle to clear.
258 * @param height the height of the rectangle to clear.
259 * @see java.awt.Graphics#fillRect(int, int, int, int)
260 * @see java.awt.Graphics#drawRect
261 * @see java.awt.Graphics#setColor(java.awt.Color)
262 * @see java.awt.Graphics#setPaintMode
263 * @see java.awt.Graphics#setXORMode(java.awt.Color)
264 */
265 public void clearRect(int x, int y, int width, int height) {
266
267 fill(new Rectangle2D.Float(x, y, width, height), getBackground());
268 }
269
270 /**
271 * Draws an outlined round-cornered rectangle using this graphics
272 * context's current color. The left and right edges of the rectangle
273 * are at <code>x</code> and <code>x + width</code>,
274 * respectively. The top and bottom edges of the rectangle are at
275 * <code>y</code> and <code>y + height</code>.
276 * @param x the <i>x</i> coordinate of the rectangle to be drawn.
277 * @param y the <i>y</i> coordinate of the rectangle to be drawn.
278 * @param width the width of the rectangle to be drawn.
279 * @param height the height of the rectangle to be drawn.
280 * @param arcWidth the horizontal diameter of the arc
281 * at the four corners.
282 * @param arcHeight the vertical diameter of the arc
283 * at the four corners.
284 * @see java.awt.Graphics#fillRoundRect
285 */
286 public void drawRoundRect(int x, int y, int width, int height,
287 int arcWidth, int arcHeight) {
288
289 draw(new RoundRectangle2D.Float(x, y,
290 width, height,
291 arcWidth, arcHeight));
292 }
293
294
295 /**
296 * Fills the specified rounded corner rectangle with the current color.
297 * The left and right edges of the rectangle
298 * are at <code>x</code> and <code>x + width - 1</code>,
299 * respectively. The top and bottom edges of the rectangle are at
300 * <code>y</code> and <code>y + height - 1</code>.
301 * @param x the <i>x</i> coordinate of the rectangle to be filled.
302 * @param y the <i>y</i> coordinate of the rectangle to be filled.
303 * @param width the width of the rectangle to be filled.
304 * @param height the height of the rectangle to be filled.
305 * @param arcWidth the horizontal diameter
306 * of the arc at the four corners.
307 * @param arcHeight the vertical diameter
308 * of the arc at the four corners.
309 * @see java.awt.Graphics#drawRoundRect
310 */
311 public void fillRoundRect(int x, int y, int width, int height,
312 int arcWidth, int arcHeight) {
313
314 fill(new RoundRectangle2D.Float(x, y,
315 width, height,
316 arcWidth, arcHeight));
317 }
318
319 /**
320 * Draws the outline of an oval.
321 * The result is a circle or ellipse that fits within the
322 * rectangle specified by the <code>x</code>, <code>y</code>,
323 * <code>width</code>, and <code>height</code> arguments.
324 * <p>
325 * The oval covers an area that is
326 * <code>width + 1</code> pixels wide
327 * and <code>height + 1</code> pixels tall.
328 * @param x the <i>x</i> coordinate of the upper left
329 * corner of the oval to be drawn.
330 * @param y the <i>y</i> coordinate of the upper left
331 * corner of the oval to be drawn.
332 * @param width the width of the oval to be drawn.
333 * @param height the height of the oval to be drawn.
334 * @see java.awt.Graphics#fillOval
335 * @since JDK1.0
336 */
337 public void drawOval(int x, int y, int width, int height) {
338 draw(new Ellipse2D.Float(x, y, width, height));
339 }
340
341 /**
342 * Fills an oval bounded by the specified rectangle with the
343 * current color.
344 * @param x the <i>x</i> coordinate of the upper left corner
345 * of the oval to be filled.
346 * @param y the <i>y</i> coordinate of the upper left corner
347 * of the oval to be filled.
348 * @param width the width of the oval to be filled.
349 * @param height the height of the oval to be filled.
350 * @see java.awt.Graphics#drawOval
351 */
352 public void fillOval(int x, int y, int width, int height){
353
354 fill(new Ellipse2D.Float(x, y, width, height));
355 }
356
357 /**
358 * Draws the outline of a circular or elliptical arc
359 * covering the specified rectangle.
360 * <p>
361 * The resulting arc begins at <code>startAngle</code> and extends
362 * for <code>arcAngle</code> degrees, using the current color.
363 * Angles are interpreted such that 0 degrees
364 * is at the 3 o'clock position.
365 * A positive value indicates a counter-clockwise rotation
366 * while a negative value indicates a clockwise rotation.
367 * <p>
368 * The center of the arc is the center of the rectangle whose origin
369 * is (<i>x</i>, <i>y</i>) and whose size is specified by the
370 * <code>width</code> and <code>height</code> arguments.
371 * <p>
372 * The resulting arc covers an area
373 * <code>width + 1</code> pixels wide
374 * by <code>height + 1</code> pixels tall.
375 * <p>
376 * The angles are specified relative to the non-square extents of
377 * the bounding rectangle such that 45 degrees always falls on the
378 * line from the center of the ellipse to the upper right corner of
379 * the bounding rectangle. As a result, if the bounding rectangle is
380 * noticeably longer in one axis than the other, the angles to the
381 * start and end of the arc segment will be skewed farther along the
382 * longer axis of the bounds.
383 * @param x the <i>x</i> coordinate of the
384 * upper-left corner of the arc to be drawn.
385 * @param y the <i>y</i> coordinate of the
386 * upper-left corner of the arc to be drawn.
387 * @param width the width of the arc to be drawn.
388 * @param height the height of the arc to be drawn.
389 * @param startAngle the beginning angle.
390 * @param arcAngle the angular extent of the arc,
391 * relative to the start angle.
392 * @see java.awt.Graphics#fillArc
393 */
394 public void drawArc(int x, int y, int width, int height,
395 int startAngle, int arcAngle) {
396 draw(new Arc2D.Float(x, y, width, height,
397 startAngle, arcAngle,
398 Arc2D.OPEN));
399 }
400
401
402 /**
403 * Fills a circular or elliptical arc covering the specified rectangle.
404 * <p>
405 * The resulting arc begins at <code>startAngle</code> and extends
406 * for <code>arcAngle</code> degrees.
407 * Angles are interpreted such that 0 degrees
408 * is at the 3 o'clock position.
409 * A positive value indicates a counter-clockwise rotation
410 * while a negative value indicates a clockwise rotation.
411 * <p>
412 * The center of the arc is the center of the rectangle whose origin
413 * is (<i>x</i>, <i>y</i>) and whose size is specified by the
414 * <code>width</code> and <code>height</code> arguments.
415 * <p>
416 * The resulting arc covers an area
417 * <code>width + 1</code> pixels wide
418 * by <code>height + 1</code> pixels tall.
419 * <p>
420 * The angles are specified relative to the non-square extents of
421 * the bounding rectangle such that 45 degrees always falls on the
422 * line from the center of the ellipse to the upper right corner of
423 * the bounding rectangle. As a result, if the bounding rectangle is
424 * noticeably longer in one axis than the other, the angles to the
425 * start and end of the arc segment will be skewed farther along the
426 * longer axis of the bounds.
427 * @param x the <i>x</i> coordinate of the
428 * upper-left corner of the arc to be filled.
429 * @param y the <i>y</i> coordinate of the
430 * upper-left corner of the arc to be filled.
431 * @param width the width of the arc to be filled.
432 * @param height the height of the arc to be filled.
433 * @param startAngle the beginning angle.
434 * @param arcAngle the angular extent of the arc,
435 * relative to the start angle.
436 * @see java.awt.Graphics#drawArc
437 */
438 public void fillArc(int x, int y, int width, int height,
439 int startAngle, int arcAngle) {
440
441 fill(new Arc2D.Float(x, y, width, height,
442 startAngle, arcAngle,
443 Arc2D.PIE));
444 }
445
446 /**
447 * Draws a sequence of connected lines defined by
448 * arrays of <i>x</i> and <i>y</i> coordinates.
449 * Each pair of (<i>x</i>, <i>y</i>) coordinates defines a point.
450 * The figure is not closed if the first point
451 * differs from the last point.
452 * @param xPoints an array of <i>x</i> points
453 * @param yPoints an array of <i>y</i> points
454 * @param nPoints the total number of points
455 * @see java.awt.Graphics#drawPolygon(int[], int[], int)
456 * @since JDK1.1
457 */
458 public void drawPolyline(int xPoints[], int yPoints[],
459 int nPoints) {
460 float fromX;
461 float fromY;
462 float toX;
463 float toY;
464
465 if (nPoints > 0) {
466 fromX = xPoints[0];
467 fromY = yPoints[0];
468 for(int i = 1; i < nPoints; i++) {
469 toX = xPoints[i];
470 toY = yPoints[i];
471 draw(new Line2D.Float(fromX, fromY, toX, toY));
472 fromX = toX;
473 fromY = toY;
474 }
475 }
476
477 }
478
479
480 /**
481 * Draws a closed polygon defined by
482 * arrays of <i>x</i> and <i>y</i> coordinates.
483 * Each pair of (<i>x</i>, <i>y</i>) coordinates defines a point.
484 * <p>
485 * This method draws the polygon defined by <code>nPoint</code> line
486 * segments, where the first <code>nPoint - 1</code>
487 * line segments are line segments from
488 * <code>(xPoints[i - 1], yPoints[i - 1])</code>
489 * to <code>(xPoints[i], yPoints[i])</code>, for
490 * 1 ≤ <i>i</i> ≤ <code>nPoints</code>.
491 * The figure is automatically closed by drawing a line connecting
492 * the final point to the first point, if those points are different.
493 * @param xPoints a an array of <code>x</code> coordinates.
494 * @param yPoints a an array of <code>y</code> coordinates.
495 * @param nPoints a the total number of points.
496 * @see java.awt.Graphics#fillPolygon
497 * @see java.awt.Graphics#drawPolyline
498 */
499 public void drawPolygon(int xPoints[], int yPoints[],
500 int nPoints) {
501
502 draw(new Polygon(xPoints, yPoints, nPoints));
503 }
504
505 /**
506 * Draws the outline of a polygon defined by the specified
507 * <code>Polygon</code> object.
508 * @param p the polygon to draw.
509 * @see java.awt.Graphics#fillPolygon
510 * @see java.awt.Graphics#drawPolyline
511 */
512 public void drawPolygon(Polygon p) {
513 draw(p);
514 }
515
516 /**
517 * Fills a closed polygon defined by
518 * arrays of <i>x</i> and <i>y</i> coordinates.
519 * <p>
520 * This method draws the polygon defined by <code>nPoint</code> line
521 * segments, where the first <code>nPoint - 1</code>
522 * line segments are line segments from
523 * <code>(xPoints[i - 1], yPoints[i - 1])</code>
524 * to <code>(xPoints[i], yPoints[i])</code>, for
525 * 1 ≤ <i>i</i> ≤ <code>nPoints</code>.
526 * The figure is automatically closed by drawing a line connecting
527 * the final point to the first point, if those points are different.
528 * <p>
529 * The area inside the polygon is defined using an
530 * even-odd fill rule, also known as the alternating rule.
531 * @param xPoints a an array of <code>x</code> coordinates.
532 * @param yPoints a an array of <code>y</code> coordinates.
533 * @param nPoints a the total number of points.
534 * @see java.awt.Graphics#drawPolygon(int[], int[], int)
535 */
536 public void fillPolygon(int xPoints[], int yPoints[],
537 int nPoints) {
538
539 fill(new Polygon(xPoints, yPoints, nPoints));
540 }
541
542
543 /**
544 * Fills the polygon defined by the specified Polygon object with
545 * the graphics context's current color.
546 * <p>
547 * The area inside the polygon is defined using an
548 * even-odd fill rule, also known as the alternating rule.
549 * @param p the polygon to fill.
550 * @see java.awt.Graphics#drawPolygon(int[], int[], int)
551 */
552 public void fillPolygon(Polygon p) {
553
554 fill(p);
555 }
556
557 /**
558 * Draws the text given by the specified string, using this
559 * graphics context's current font and color. The baseline of the
560 * first character is at position (<i>x</i>, <i>y</i>) in this
561 * graphics context's coordinate system.
562 * @param str the string to be drawn.
563 * @param x the <i>x</i> coordinate.
564 * @param y the <i>y</i> coordinate.
565 * @see java.awt.Graphics#drawBytes
566 * @see java.awt.Graphics#drawChars
567 * @since JDK1.0
568 */
569 public void drawString(String str, int x, int y) {
570 drawString(str, (float) x, (float) y);
571 }
572
573 public void drawString(String str, float x, float y) {
574 if (str.length() == 0) {
575 return;
576 }
577 TextLayout layout =
578 new TextLayout(str, getFont(), getFontRenderContext());
579 layout.draw(this, x, y);
580 }
581
582 protected void drawString(String str, float x, float y,
583 Font font, FontRenderContext frc, float w) {
584 TextLayout layout =
585 new TextLayout(str, font, frc);
586 Shape textShape =
587 layout.getOutline(AffineTransform.getTranslateInstance(x, y));
588 fill(textShape);
589 }
590
591 /**
592 * Draws the text given by the specified iterator, using this
593 * graphics context's current color. The iterator has to specify a font
594 * for each character. The baseline of the
595 * first character is at position (<i>x</i>, <i>y</i>) in this
596 * graphics context's coordinate system.
597 * @param iterator the iterator whose text is to be drawn
598 * @param x the <i>x</i> coordinate.
599 * @param y the <i>y</i> coordinate.
600 * @see java.awt.Graphics#drawBytes
601 * @see java.awt.Graphics#drawChars
602 */
603 public void drawString(AttributedCharacterIterator iterator,
604 int x, int y) {
605 drawString(iterator, (float) x, (float) y);
606 }
607 public void drawString(AttributedCharacterIterator iterator,
608 float x, float y) {
609 if (iterator == null) {
610 throw
611 new NullPointerException("attributedcharacteriterator is null");
612 }
613 TextLayout layout =
614 new TextLayout(iterator, getFontRenderContext());
615 layout.draw(this, x, y);
616 }
617
618 /**
619 * Draws a GlyphVector.
620 * The rendering attributes applied include the clip, transform,
621 * paint or color, and composite attributes. The GlyphVector specifies
622 * individual glyphs from a Font.
623 * @param g The GlyphVector to be drawn.
624 * @param x,y The coordinates where the glyphs should be drawn.
625 * @see #setPaint
626 * @see java.awt.Graphics#setColor
627 * @see #transform
628 * @see #setTransform
629 * @see #setComposite
630 * @see #clip
631 * @see #setClip
632 */
633 public void drawGlyphVector(GlyphVector g,
634 float x,
635 float y) {
636
637 /* We should not reach here if printingGlyphVector is already true.
638 * Add an assert so this can be tested if need be.
639 * But also ensure that we do at least render properly by filling
640 * the outline.
641 */
642 if (printingGlyphVector) {
643 assert !printingGlyphVector; // ie false.
644 fill(g.getOutline(x, y));
645 return;
646 }
647
648 try {
649 printingGlyphVector = true;
650 if (RasterPrinterJob.shapeTextProp ||
651 !printedSimpleGlyphVector(g, x, y)) {
652 fill(g.getOutline(x, y));
653 }
654 } finally {
655 printingGlyphVector = false;
656 }
657 }
658
659 protected static SoftReference<Hashtable<Font2DHandle,Object>>
660 fontMapRef = new SoftReference<Hashtable<Font2DHandle,Object>>(null);
661
662 protected int platformFontCount(Font font, String str) {
663 return 0;
664 }
665
666 /**
667 * Default implementation returns false.
668 * Callers of this method must always be prepared for this,
669 * and delegate to outlines or some other solution.
670 */
671 protected boolean printGlyphVector(GlyphVector gv, float x, float y) {
672 return false;
673 }
674
675 /* GlyphVectors are usually encountered because TextLayout is in use.
676 * Some times TextLayout is needed to handle complex text or some
677 * rendering attributes trigger it.
678 * We try to print GlyphVectors by reconstituting into a String,
679 * as that is most recoverable for applications that export to formats
680 * such as Postscript or PDF. In some cases (eg where its not complex
681 * text and its just that positions aren't what we'd expect) we print
682 * one character at a time. positioning individually.
683 * Failing that, if we can directly send glyph codes to the printer
684 * then we do that (printGlyphVector).
685 * As a last resort we return false and let the caller print as filled
686 * shapes.
687 */
688 boolean printedSimpleGlyphVector(GlyphVector g, float x, float y) {
689
690 int flags = g.getLayoutFlags();
691
692 /* We can't handle RTL, re-ordering, complex glyphs etc by
693 * reconstituting glyphs into a String. So if any flags besides
694 * position adjustments are set, see if we can directly
695 * print the GlyphVector as glyph codes, using the positions
696 * layout has assigned. If that fails return false;
697 */
698 if (flags != 0 && flags != GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) {
699 return printGlyphVector(g, x, y);
700 }
701
702 Font font = g.getFont();
703 Font2D font2D = FontUtilities.getFont2D(font);
704 if (font2D.handle.font2D != font2D) {
705 /* suspicious, may be a bad font. lets bail */
706 return false;
707 }
708 Hashtable<Font2DHandle,Object> fontMap;
709 synchronized (PathGraphics.class) {
710 fontMap = fontMapRef.get();
711 if (fontMap == null) {
712 fontMap = new Hashtable<Font2DHandle,Object>();
713 fontMapRef =
714 new SoftReference<Hashtable<Font2DHandle,Object>>(fontMap);
715 }
716 }
717
718 int numGlyphs = g.getNumGlyphs();
719 int[] glyphCodes = g.getGlyphCodes(0, numGlyphs, null);
720
721 char[] glyphToCharMap = null;
722 char[][] mapArray = null;
723 CompositeFont cf = null;
724
725 /* Build the needed maps for this font in a synchronized block */
726 synchronized (fontMap) {
727 if (font2D instanceof CompositeFont) {
728 cf = (CompositeFont)font2D;
729 int numSlots = cf.getNumSlots();
730 mapArray = (char[][])fontMap.get(font2D.handle);
731 if (mapArray == null) {
732 mapArray = new char[numSlots][];
733 fontMap.put(font2D.handle, mapArray);
734 }
735 for (int i=0; i<numGlyphs;i++) {
736 int slot = glyphCodes[i] >>> 24;
737 if (slot >= numSlots) { /* shouldn't happen */
738 return false;
739 }
740 if (mapArray[slot] == null) {
741 Font2D slotFont = cf.getSlotFont(slot);
742 char[] map = (char[])fontMap.get(slotFont.handle);
743 if (map == null) {
744 map = getGlyphToCharMapForFont(slotFont);
745 }
746 mapArray[slot] = map;
747 }
748 }
749 } else {
750 glyphToCharMap = (char[])fontMap.get(font2D.handle);
751 if (glyphToCharMap == null) {
752 glyphToCharMap = getGlyphToCharMapForFont(font2D);
753 fontMap.put(font2D.handle, glyphToCharMap);
754 }
755 }
756 }
757
758 char[] chars = new char[numGlyphs];
759 if (cf != null) {
760 for (int i=0; i<numGlyphs; i++) {
761 int gc = glyphCodes[i];
762 char[] map = mapArray[gc >>> 24];
763 gc = gc & 0xffffff;
764 if (map == null) {
765 return false;
766 }
767 /* X11 symbol & dingbats fonts used only for global metrics,
768 * so the glyph codes we have really refer to Lucida Sans
769 * Regular.
770 * So its possible the glyph code may appear out of range.
771 * Note that later on we double-check the glyph codes that
772 * we get from re-creating the GV from the string are the
773 * same as those we started with.
774 *
775 * If the glyphcode is INVISIBLE_GLYPH_ID then this may
776 * be \t, \n or \r which are mapped to that by layout.
777 * This is a case we can handle. It doesn't matter what
778 * character we use (we use \n) so long as layout maps it
779 * back to this in the verification, since the invisible
780 * glyph isn't visible :)
781 */
782 char ch;
783 if (gc == CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
784 ch = '\n';
785 } else if (gc < 0 || gc >= map.length) {
786 return false;
787 } else {
788 ch = map[gc];
789 }
790 if (ch != CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
791 chars[i] = ch;
792 } else {
793 return false;
794 }
795 }
796 } else {
797 for (int i=0; i<numGlyphs; i++) {
798 int gc = glyphCodes[i];
799 char ch;
800 if (gc == CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
801 ch = '\n';
802 } else if (gc < 0 || gc >= glyphToCharMap.length) {
803 return false;
804 } else {
805 ch = glyphToCharMap[gc];
806 }
807 if (ch != CharToGlyphMapper.INVISIBLE_GLYPH_ID) {
808 chars[i] = ch;
809 } else {
810 return false;
811 }
812 }
813 }
814
815 FontRenderContext gvFrc = g.getFontRenderContext();
816 GlyphVector gv2 = font.createGlyphVector(gvFrc, chars);
817 if (gv2.getNumGlyphs() != numGlyphs) {
818 return printGlyphVector(g, x, y);
819 }
820 int[] glyphCodes2 = gv2.getGlyphCodes(0, numGlyphs, null);
821 /*
822 * Needed to double-check remapping of X11 symbol & dingbats.
823 */
824 for (int i=0; i<numGlyphs; i++) {
825 if (glyphCodes[i] != glyphCodes2[i]) {
826 return printGlyphVector(g, x, y);
827 }
828 }
829
830 FontRenderContext g2dFrc = getFontRenderContext();
831 boolean compatibleFRC = gvFrc.equals(g2dFrc);
832 /* If differ only in specifying A-A or a translation, these are
833 * also compatible FRC's, and we can do one drawString call.
834 */
835 if (!compatibleFRC &&
836 gvFrc.usesFractionalMetrics() == g2dFrc.usesFractionalMetrics()) {
837 AffineTransform gvAT = gvFrc.getTransform();
838 AffineTransform g2dAT = getTransform();
839 double[] gvMatrix = new double[4];
840 double[] g2dMatrix = new double[4];
841 gvAT.getMatrix(gvMatrix);
842 g2dAT.getMatrix(g2dMatrix);
843 compatibleFRC = true;
844 for (int i=0;i<4;i++) {
845 if (gvMatrix[i] != g2dMatrix[i]) {
846 compatibleFRC = false;
847 break;
848 }
849 }
850 }
851
852 String str = new String(chars, 0, numGlyphs);
853 int numFonts = platformFontCount(font, str);
854 if (numFonts == 0) {
855 return false;
856 }
857
858 float[] positions = g.getGlyphPositions(0, numGlyphs, null);
859 boolean noPositionAdjustments =
860 ((flags & GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS) == 0) ||
861 samePositions(gv2, glyphCodes2, glyphCodes, positions);
862
863 /* We have to consider that the application may be directly
864 * creating a GlyphVector, rather than one being created by
865 * TextLayout or indirectly from drawString. In such a case, if the
866 * font has layout attributes, the text may measure differently
867 * when we reconstitute it into a String and ask for the length that
868 * drawString would use. For example, KERNING will be applied in such
869 * a case but that Font attribute is not applied when the application
870 * directly created a GlyphVector. So in this case we need to verify
871 * that the text measures the same in both cases - ie that the
872 * layout attribute has no effect. If it does we can't always
873 * use the drawString call unless we can coerce the drawString call
874 * into measuring and displaying the string to the same length.
875 * That is the case where there is only one font used and we can
876 * specify the overall advance of the string. (See below).
877 */
878
879 Point2D gvAdvancePt = g.getGlyphPosition(numGlyphs);
880 float gvAdvanceX = (float)gvAdvancePt.getX();
881 boolean layoutAffectsAdvance = false;
882 if (font.hasLayoutAttributes() && printingGlyphVector &&
883 noPositionAdjustments) {
884
885 /* If TRACKING is in use then the glyph vector will report
886 * position adjustments, then that ought to be sufficient to
887 * tell us we can't just ask native to do "drawString". But layout
888 * always sets the position adjustment flag, so we don't believe
889 * it and verify the positions are really different than
890 * createGlyphVector() (with no layout) would create. However
891 * inconsistently, TRACKING is applied when creating a GlyphVector,
892 * since it doesn't actually require "layout" (even though its
893 * considered a layout attribute), it just requires a fractional
894 * tweak to the[default]advances. So we need to specifically
895 * check for tracking until such time as as we can trust
896 * the GlyphVector.FLAG_HAS_POSITION_ADJUSTMENTS bit.
897 */
898 Map<TextAttribute, ?> map = font.getAttributes();
899 Object o = map.get(TextAttribute.TRACKING);
900 boolean tracking = o != null && (o instanceof Number) &&
901 (((Number)o).floatValue() != 0f);
902
903 if (tracking) {
904 noPositionAdjustments = false;
905 } else {
906 Rectangle2D bounds = font.getStringBounds(str, gvFrc);
907 float strAdvanceX = (float)bounds.getWidth();
908 if (Math.abs(strAdvanceX - gvAdvanceX) > 0.00001) {
909 layoutAffectsAdvance = true;
910 }
911 }
912 }
913
914 if (compatibleFRC && noPositionAdjustments && !layoutAffectsAdvance) {
915 drawString(str, x, y, font, gvFrc, 0f);
916 return true;
917 }
918
919 /* If positions have not been explicitly assigned, we can
920 * ask the string to be drawn adjusted to this width.
921 * This call is supported only in the PS generator.
922 * GDI has API to specify the advance for each glyph in a
923 * string which could be used here too, but that is not yet
924 * implemented, and we'd need to update the signature of the
925 * drawString method to take the advances (ie relative positions)
926 * and use that instead of the width.
927 */
928 if (numFonts == 1 && canDrawStringToWidth() && noPositionAdjustments) {
929 drawString(str, x, y, font, gvFrc, gvAdvanceX);
930 return true;
931 }
932
933 /* In some scripts chars drawn individually do not have the
934 * same representation (glyphs) as when combined with other chars.
935 * The logic here is erring on the side of caution, in particular
936 * in including supplementary characters.
937 */
938 if (FontUtilities.isComplexText(chars, 0, chars.length)) {
939 return printGlyphVector(g, x, y);
940 }
941
942 /* If we reach here we have mapped all the glyphs back
943 * one-to-one to simple unicode chars that we know are in the font.
944 * We can call "drawChars" on each one of them in turn, setting
945 * the position based on the glyph positions.
946 * There's typically overhead in this. If numGlyphs is 'large',
947 * it may even be better to try printGlyphVector() in this case.
948 * This may be less recoverable for apps, but sophisticated apps
949 * should be able to recover the text from simple glyph vectors
950 * and we can avoid penalising the more common case - although
951 * this is already a minority case.
952 */
953 if (numGlyphs > 10 && printGlyphVector(g, x, y)) {
954 return true;
955 }
956
957 for (int i=0; i<numGlyphs; i++) {
958 String s = new String(chars, i, 1);
959 drawString(s, x+positions[i*2], y+positions[i*2+1],
960 font, gvFrc, 0f);
961 }
962 return true;
963 }
964
965 /* The same codes must be in the same positions for this to return true.
966 * This would look cleaner if it took the original GV as a parameter but
967 * we already have the codes and will need to get the positions array
968 * too in most cases anyway. So its cheaper to pass them in.
969 * This call wouldn't be necessary if layout didn't always set the
970 * FLAG_HAS_POSITION_ADJUSTMENTS even if the default advances are used
971 * and there was no re-ordering (this should be fixed some day).
972 */
973 private boolean samePositions(GlyphVector gv, int[] gvcodes,
974 int[] origCodes, float[] origPositions) {
975
976 int numGlyphs = gv.getNumGlyphs();
977 float[] gvpos = gv.getGlyphPositions(0, numGlyphs, null);
978
979 /* this shouldn't happen here, but just in case */
980 if (numGlyphs != gvcodes.length || /* real paranoia here */
981 origCodes.length != gvcodes.length ||
982 origPositions.length != gvpos.length) {
983 return false;
984 }
985
986 for (int i=0; i<numGlyphs; i++) {
987 if (gvcodes[i] != origCodes[i] || gvpos[i] != origPositions[i]) {
988 return false;
989 }
990 }
991 return true;
992 }
993
994 protected boolean canDrawStringToWidth() {
995 return false;
996 }
997
998 /* return an array which can map glyphs back to char codes.
999 * Glyphs which aren't mapped from a simple unicode code point
1000 * will have no mapping in this array, and will be assumed to be
1001 * because of some substitution that we can't handle.
1002 */
1003 private static char[] getGlyphToCharMapForFont(Font2D font2D) {
1004 /* NB Composites report the number of glyphs in slot 0.
1005 * So if a string uses a char from a later slot, or a fallback slot,
1006 * it will not be able to use this faster path.
1007 */
1008 int numGlyphs = font2D.getNumGlyphs();
1009 int missingGlyph = font2D.getMissingGlyphCode();
1010 char[] glyphToCharMap = new char[numGlyphs];
1011 int glyph;
1012
1013 for (int i=0;i<numGlyphs; i++) {
1014 glyphToCharMap[i] = CharToGlyphMapper.INVISIBLE_GLYPH_ID;
1015 }
1016
1017 /* Consider refining the ranges to try to map by asking the font
1018 * what ranges it supports.
1019 * Since a glyph may be mapped by multiple code points, and this
1020 * code can't handle that, we always prefer the earlier code point.
1021 */
1022 for (char c=0; c<0xFFFF; c++) {
1023 if (c >= CharToGlyphMapper.HI_SURROGATE_START &&
1024 c <= CharToGlyphMapper.LO_SURROGATE_END) {
1025 continue;
1026 }
1027 glyph = font2D.charToGlyph(c);
1028 if (glyph != missingGlyph && glyph < numGlyphs &&
1029 (glyphToCharMap[glyph] ==
1030 CharToGlyphMapper.INVISIBLE_GLYPH_ID)) {
1031 glyphToCharMap[glyph] = c;
1032 }
1033 }
1034 return glyphToCharMap;
1035 }
1036
1037 /**
1038 * Strokes the outline of a Shape using the settings of the current
1039 * graphics state. The rendering attributes applied include the
1040 * clip, transform, paint or color, composite and stroke attributes.
1041 * @param s The shape to be drawn.
1042 * @see #setStroke
1043 * @see #setPaint
1044 * @see java.awt.Graphics#setColor
1045 * @see #transform
1046 * @see #setTransform
1047 * @see #clip
1048 * @see #setClip
1049 * @see #setComposite
1050 */
1051 public void draw(Shape s) {
1052
1053 fill(getStroke().createStrokedShape(s));
1054 }
1055
1056 /**
1057 * Fills the interior of a Shape using the settings of the current
1058 * graphics state. The rendering attributes applied include the
1059 * clip, transform, paint or color, and composite.
1060 * @see #setPaint
1061 * @see java.awt.Graphics#setColor
1062 * @see #transform
1063 * @see #setTransform
1064 * @see #setComposite
1065 * @see #clip
1066 * @see #setClip
1067 */
1068 public void fill(Shape s) {
1069 Paint paint = getPaint();
1070
1071 try {
1072 fill(s, (Color) paint);
1073
1074 /* The PathGraphics class only supports filling with
1075 * solid colors and so we do not expect the cast of Paint
1076 * to Color to fail. If it does fail then something went
1077 * wrong, like the app draw a page with a solid color but
1078 * then redrew it with a Gradient.
1079 */
1080 } catch (ClassCastException e) {
1081 throw new IllegalArgumentException("Expected a Color instance");
1082 }
1083 }
1084
1085 public void fill(Shape s, Color color) {
1086 AffineTransform deviceTransform = getTransform();
1087
1088 if (getClip() != null) {
1089 deviceClip(getClip().getPathIterator(deviceTransform));
1090 }
1091 deviceFill(s.getPathIterator(deviceTransform), color);
1092 }
1093
1094 /**
1095 * Fill the path defined by <code>pathIter</code>
1096 * with the specified color.
1097 * The path is provided in device coordinates.
1098 */
1099 protected abstract void deviceFill(PathIterator pathIter, Color color);
1100
1101 /*
1102 * Set the clipping path to that defined by
1103 * the passed in <code>PathIterator</code>.
1104 */
1105 protected abstract void deviceClip(PathIterator pathIter);
1106
1107 /*
1108 * Draw the outline of the rectangle without using path
1109 * if supported by platform.
1110 */
1111 protected abstract void deviceFrameRect(int x, int y,
1112 int width, int height,
1113 Color color);
1114
1115 /*
1116 * Draw a line without using path if supported by platform.
1117 */
1118 protected abstract void deviceDrawLine(int xBegin, int yBegin,
1119 int xEnd, int yEnd, Color color);
1120
1121 /*
1122 * Fill a rectangle using specified color.
1123 */
1124 protected abstract void deviceFillRect(int x, int y,
1125 int width, int height, Color color);
1126
1127 /* Obtain a BI from known implementations of java.awt.Image
1128 */
1129 protected BufferedImage getBufferedImage(Image img) {
1130 if (img instanceof BufferedImage) {
1131 // Otherwise we expect a BufferedImage to behave as a standard BI
1132 return (BufferedImage)img;
1133 } else if (img instanceof ToolkitImage) {
1134 // This can be null if the image isn't loaded yet.
1135 // This is fine as in that case our caller will return
1136 // as it will only draw a fully loaded image
1137 return ((ToolkitImage)img).getBufferedImage();
1138 } else if (img instanceof VolatileImage) {
1139 // VI needs to make a new BI: this is unavoidable but
1140 // I don't expect VI's to be "huge" in any case.
1141 return ((VolatileImage)img).getSnapshot();
1142 } else {
1143 // may be null or may be some non-standard Image which
1144 // shouldn't happen as Image is implemented by the platform
1145 // not by applications
1146 // If you add a new Image implementation to the platform you
1147 // will need to support it here similarly to VI.
1148 return null;
1149 }
1150 }
1151
1152 /**
1153 * Return true if the BufferedImage argument has non-opaque
1154 * bits in it and therefore can not be directly rendered by
1155 * GDI. Return false if the image is opaque. If this function
1156 * can not tell for sure whether the image has transparent
1157 * pixels then it assumes that it does.
1158 */
1159 protected boolean hasTransparentPixels(BufferedImage bufferedImage) {
1160 ColorModel colorModel = bufferedImage.getColorModel();
1161 boolean hasTransparency = colorModel == null
1162 ? true
1163 : colorModel.getTransparency() != ColorModel.OPAQUE;
1164
1165 /*
1166 * For the default INT ARGB check the image to see if any pixels are
1167 * really transparent. If there are no transparent pixels then the
1168 * transparency of the color model can be ignored.
1169 * We assume that IndexColorModel images have already been
1170 * checked for transparency and will be OPAQUE unless they actually
1171 * have transparent pixels present.
1172 */
1173 if (hasTransparency && bufferedImage != null) {
1174 if (bufferedImage.getType()==BufferedImage.TYPE_INT_ARGB ||
1175 bufferedImage.getType()==BufferedImage.TYPE_INT_ARGB_PRE) {
1176 DataBuffer db = bufferedImage.getRaster().getDataBuffer();
1177 SampleModel sm = bufferedImage.getRaster().getSampleModel();
1178 if (db instanceof DataBufferInt &&
1179 sm instanceof SinglePixelPackedSampleModel) {
1180 SinglePixelPackedSampleModel psm =
1181 (SinglePixelPackedSampleModel)sm;
1182 // Stealing the data array for reading only...
1183 int[] int_data =
1184 SunWritableRaster.stealData((DataBufferInt) db, 0);
1185 int x = bufferedImage.getMinX();
1186 int y = bufferedImage.getMinY();
1187 int w = bufferedImage.getWidth();
1188 int h = bufferedImage.getHeight();
1189 int stride = psm.getScanlineStride();
1190 boolean hastranspixel = false;
1191 for (int j = y; j < y+h; j++) {
1192 int yoff = j * stride;
1193 for (int i = x; i < x+w; i++) {
1194 if ((int_data[yoff+i] & 0xff000000)!=0xff000000 ) {
1195 hastranspixel = true;
1196 break;
1197 }
1198 }
1199 if (hastranspixel) {
1200 break;
1201 }
1202 }
1203 if (hastranspixel == false) {
1204 hasTransparency = false;
1205 }
1206 }
1207 }
1208 }
1209
1210 return hasTransparency;
1211 }
1212
1213 protected boolean isBitmaskTransparency(BufferedImage bufferedImage) {
1214 ColorModel colorModel = bufferedImage.getColorModel();
1215 return (colorModel != null &&
1216 colorModel.getTransparency() == ColorModel.BITMASK);
1217 }
1218
1219
1220 /* An optimisation for the special case of ICM images which have
1221 * bitmask transparency.
1222 */
1223 protected boolean drawBitmaskImage(BufferedImage bufferedImage,
1224 AffineTransform xform,
1225 Color bgcolor,
1226 int srcX, int srcY,
1227 int srcWidth, int srcHeight) {
1228
1229 ColorModel colorModel = bufferedImage.getColorModel();
1230 IndexColorModel icm;
1231 int [] pixels;
1232
1233 if (!(colorModel instanceof IndexColorModel)) {
1234 return false;
1235 } else {
1236 icm = (IndexColorModel)colorModel;
1237 }
1238
1239 if (colorModel.getTransparency() != ColorModel.BITMASK) {
1240 return false;
1241 }
1242
1243 // to be compatible with 1.1 printing which treated b/g colors
1244 // with alpha 128 as opaque
1245 if (bgcolor != null && bgcolor.getAlpha() < 128) {
1246 return false;
1247 }
1248
1249 if ((xform.getType()
1250 & ~( AffineTransform.TYPE_UNIFORM_SCALE
1251 | AffineTransform.TYPE_TRANSLATION
1252 | AffineTransform.TYPE_QUADRANT_ROTATION
1253 )) != 0) {
1254 return false;
1255 }
1256
1257 if ((getTransform().getType()
1258 & ~( AffineTransform.TYPE_UNIFORM_SCALE
1259 | AffineTransform.TYPE_TRANSLATION
1260 | AffineTransform.TYPE_QUADRANT_ROTATION
1261 )) != 0) {
1262 return false;
1263 }
1264
1265 BufferedImage subImage = null;
1266 Raster raster = bufferedImage.getRaster();
1267 int transpixel = icm.getTransparentPixel();
1268 byte[] alphas = new byte[icm.getMapSize()];
1269 icm.getAlphas(alphas);
1270 if (transpixel >= 0) {
1271 alphas[transpixel] = 0;
1272 }
1273
1274 /* don't just use srcWidth & srcHeight from application - they
1275 * may exceed the extent of the image - may need to clip.
1276 * The image xform will ensure that points are still mapped properly.
1277 */
1278 int rw = raster.getWidth();
1279 int rh = raster.getHeight();
1280 if (srcX > rw || srcY > rh) {
1281 return false;
1282 }
1283 int right, bottom, wid, hgt;
1284 if (srcX+srcWidth > rw) {
1285 right = rw;
1286 wid = right - srcX;
1287 } else {
1288 right = srcX+srcWidth;
1289 wid = srcWidth;
1290 }
1291 if (srcY+srcHeight > rh) {
1292 bottom = rh;
1293 hgt = bottom - srcY;
1294 } else {
1295 bottom = srcY+srcHeight;
1296 hgt = srcHeight;
1297 }
1298 pixels = new int[wid];
1299 for (int j=srcY; j<bottom; j++) {
1300 int startx = -1;
1301 raster.getPixels(srcX, j, wid, 1, pixels);
1302 for (int i=srcX; i<right; i++) {
1303 if (alphas[pixels[i-srcX]] == 0) {
1304 if (startx >=0) {
1305 subImage = bufferedImage.getSubimage(startx, j,
1306 i-startx, 1);
1307 xform.translate(startx, j);
1308 drawImageToPlatform(subImage, xform, bgcolor,
1309 0, 0, i-startx, 1, true);
1310 xform.translate(-startx, -j);
1311 startx = -1;
1312 }
1313 } else if (startx < 0) {
1314 startx = i;
1315 }
1316 }
1317 if (startx >= 0) {
1318 subImage = bufferedImage.getSubimage(startx, j,
1319 right - startx, 1);
1320 xform.translate(startx, j);
1321 drawImageToPlatform(subImage, xform, bgcolor,
1322 0, 0, right - startx, 1, true);
1323 xform.translate(-startx, -j);
1324 }
1325 }
1326 return true;
1327 }
1328
1329
1330
1331 /**
1332 * The various <code>drawImage()</code> methods for
1333 * <code>PathGraphics</code> are all decomposed
1334 * into an invocation of <code>drawImageToPlatform</code>.
1335 * The portion of the passed in image defined by
1336 * <code>srcX, srcY, srcWidth, and srcHeight</code>
1337 * is transformed by the supplied AffineTransform and
1338 * drawn using PS to the printer context.
1339 *
1340 * @param img The image to be drawn.
1341 * This method does nothing if <code>img</code> is null.
1342 * @param xform Used to tranform the image before drawing.
1343 * This can be null.
1344 * @param bgcolor This color is drawn where the image has transparent
1345 * pixels. If this parameter is null then the
1346 * pixels already in the destination should show
1347 * through.
1348 * @param srcX With srcY this defines the upper-left corner
1349 * of the portion of the image to be drawn.
1350 *
1351 * @param srcY With srcX this defines the upper-left corner
1352 * of the portion of the image to be drawn.
1353 * @param srcWidth The width of the portion of the image to
1354 * be drawn.
1355 * @param srcHeight The height of the portion of the image to
1356 * be drawn.
1357 * @param handlingTransparency if being recursively called to
1358 * print opaque region of transparent image
1359 */
1360 protected abstract boolean
1361 drawImageToPlatform(Image img, AffineTransform xform,
1362 Color bgcolor,
1363 int srcX, int srcY,
1364 int srcWidth, int srcHeight,
1365 boolean handlingTransparency);
1366
1367 /**
1368 * Draws as much of the specified image as is currently available.
1369 * The image is drawn with its top-left corner at
1370 * (<i>x</i>, <i>y</i>) in this graphics context's coordinate
1371 * space. Transparent pixels in the image do not affect whatever
1372 * pixels are already there.
1373 * <p>
1374 * This method returns immediately in all cases, even if the
1375 * complete image has not yet been loaded, and it has not been dithered
1376 * and converted for the current output device.
1377 * <p>
1378 * If the image has not yet been completely loaded, then
1379 * <code>drawImage</code> returns <code>false</code>. As more of
1380 * the image becomes available, the process that draws the image notifies
1381 * the specified image observer.
1382 * @param img the specified image to be drawn.
1383 * @param x the <i>x</i> coordinate.
1384 * @param y the <i>y</i> coordinate.
1385 * @param observer object to be notified as more of
1386 * the image is converted.
1387 * @see java.awt.Image
1388 * @see java.awt.image.ImageObserver
1389 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1390 * @since JDK1.0
1391 */
1392 public boolean drawImage(Image img, int x, int y,
1393 ImageObserver observer) {
1394
1395 return drawImage(img, x, y, null, observer);
1396 }
1397
1398 /**
1399 * Draws as much of the specified image as has already been scaled
1400 * to fit inside the specified rectangle.
1401 * <p>
1402 * The image is drawn inside the specified rectangle of this
1403 * graphics context's coordinate space, and is scaled if
1404 * necessary. Transparent pixels do not affect whatever pixels
1405 * are already there.
1406 * <p>
1407 * This method returns immediately in all cases, even if the
1408 * entire image has not yet been scaled, dithered, and converted
1409 * for the current output device.
1410 * If the current output representation is not yet complete, then
1411 * <code>drawImage</code> returns <code>false</code>. As more of
1412 * the image becomes available, the process that draws the image notifies
1413 * the image observer by calling its <code>imageUpdate</code> method.
1414 * <p>
1415 * A scaled version of an image will not necessarily be
1416 * available immediately just because an unscaled version of the
1417 * image has been constructed for this output device. Each size of
1418 * the image may be cached separately and generated from the original
1419 * data in a separate image production sequence.
1420 * @param img the specified image to be drawn.
1421 * @param x the <i>x</i> coordinate.
1422 * @param y the <i>y</i> coordinate.
1423 * @param width the width of the rectangle.
1424 * @param height the height of the rectangle.
1425 * @param observer object to be notified as more of
1426 * the image is converted.
1427 * @see java.awt.Image
1428 * @see java.awt.image.ImageObserver
1429 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1430 * @since JDK1.0
1431 */
1432 public boolean drawImage(Image img, int x, int y,
1433 int width, int height,
1434 ImageObserver observer) {
1435
1436 return drawImage(img, x, y, width, height, null, observer);
1437
1438 }
1439
1440 /*
1441 * Draws as much of the specified image as is currently available.
1442 * The image is drawn with its top-left corner at
1443 * (<i>x</i>, <i>y</i>) in this graphics context's coordinate
1444 * space. Transparent pixels are drawn in the specified
1445 * background color.
1446 * <p>
1447 * This operation is equivalent to filling a rectangle of the
1448 * width and height of the specified image with the given color and then
1449 * drawing the image on top of it, but possibly more efficient.
1450 * <p>
1451 * This method returns immediately in all cases, even if the
1452 * complete image has not yet been loaded, and it has not been dithered
1453 * and converted for the current output device.
1454 * <p>
1455 * If the image has not yet been completely loaded, then
1456 * <code>drawImage</code> returns <code>false</code>. As more of
1457 * the image becomes available, the process that draws the image notifies
1458 * the specified image observer.
1459 * @param img the specified image to be drawn.
1460 * This method does nothing if <code>img</code> is null.
1461 * @param x the <i>x</i> coordinate.
1462 * @param y the <i>y</i> coordinate.
1463 * @param bgcolor the background color to paint under the
1464 * non-opaque portions of the image.
1465 * In this WPathGraphics implementation,
1466 * this parameter can be null in which
1467 * case that background is made a transparent
1468 * white.
1469 * @param observer object to be notified as more of
1470 * the image is converted.
1471 * @see java.awt.Image
1472 * @see java.awt.image.ImageObserver
1473 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1474 * @since JDK1.0
1475 */
1476 public boolean drawImage(Image img, int x, int y,
1477 Color bgcolor,
1478 ImageObserver observer) {
1479
1480 if (img == null) {
1481 return true;
1482 }
1483
1484 boolean result;
1485 int srcWidth = img.getWidth(null);
1486 int srcHeight = img.getHeight(null);
1487
1488 if (srcWidth < 0 || srcHeight < 0) {
1489 result = false;
1490 } else {
1491 result = drawImage(img, x, y, srcWidth, srcHeight, bgcolor, observer);
1492 }
1493
1494 return result;
1495 }
1496
1497 /**
1498 * Draws as much of the specified image as has already been scaled
1499 * to fit inside the specified rectangle.
1500 * <p>
1501 * The image is drawn inside the specified rectangle of this
1502 * graphics context's coordinate space, and is scaled if
1503 * necessary. Transparent pixels are drawn in the specified
1504 * background color.
1505 * This operation is equivalent to filling a rectangle of the
1506 * width and height of the specified image with the given color and then
1507 * drawing the image on top of it, but possibly more efficient.
1508 * <p>
1509 * This method returns immediately in all cases, even if the
1510 * entire image has not yet been scaled, dithered, and converted
1511 * for the current output device.
1512 * If the current output representation is not yet complete then
1513 * <code>drawImage</code> returns <code>false</code>. As more of
1514 * the image becomes available, the process that draws the image notifies
1515 * the specified image observer.
1516 * <p>
1517 * A scaled version of an image will not necessarily be
1518 * available immediately just because an unscaled version of the
1519 * image has been constructed for this output device. Each size of
1520 * the image may be cached separately and generated from the original
1521 * data in a separate image production sequence.
1522 * @param img the specified image to be drawn.
1523 * This method does nothing if <code>img</code> is null.
1524 * @param x the <i>x</i> coordinate.
1525 * @param y the <i>y</i> coordinate.
1526 * @param width the width of the rectangle.
1527 * @param height the height of the rectangle.
1528 * @param bgcolor the background color to paint under the
1529 * non-opaque portions of the image.
1530 * @param observer object to be notified as more of
1531 * the image is converted.
1532 * @see java.awt.Image
1533 * @see java.awt.image.ImageObserver
1534 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1535 * @since JDK1.0
1536 */
1537 public boolean drawImage(Image img, int x, int y,
1538 int width, int height,
1539 Color bgcolor,
1540 ImageObserver observer) {
1541
1542 if (img == null) {
1543 return true;
1544 }
1545
1546 boolean result;
1547 int srcWidth = img.getWidth(null);
1548 int srcHeight = img.getHeight(null);
1549
1550 if (srcWidth < 0 || srcHeight < 0) {
1551 result = false;
1552 } else {
1553 result = drawImage(img,
1554 x, y, x + width, y + height,
1555 0, 0, srcWidth, srcHeight,
1556 observer);
1557 }
1558
1559 return result;
1560 }
1561
1562 /**
1563 * Draws as much of the specified area of the specified image as is
1564 * currently available, scaling it on the fly to fit inside the
1565 * specified area of the destination drawable surface. Transparent pixels
1566 * do not affect whatever pixels are already there.
1567 * <p>
1568 * This method returns immediately in all cases, even if the
1569 * image area to be drawn has not yet been scaled, dithered, and converted
1570 * for the current output device.
1571 * If the current output representation is not yet complete then
1572 * <code>drawImage</code> returns <code>false</code>. As more of
1573 * the image becomes available, the process that draws the image notifies
1574 * the specified image observer.
1575 * <p>
1576 * This method always uses the unscaled version of the image
1577 * to render the scaled rectangle and performs the required
1578 * scaling on the fly. It does not use a cached, scaled version
1579 * of the image for this operation. Scaling of the image from source
1580 * to destination is performed such that the first coordinate
1581 * of the source rectangle is mapped to the first coordinate of
1582 * the destination rectangle, and the second source coordinate is
1583 * mapped to the second destination coordinate. The subimage is
1584 * scaled and flipped as needed to preserve those mappings.
1585 * @param img the specified image to be drawn
1586 * @param dx1 the <i>x</i> coordinate of the first corner of the
1587 * destination rectangle.
1588 * @param dy1 the <i>y</i> coordinate of the first corner of the
1589 * destination rectangle.
1590 * @param dx2 the <i>x</i> coordinate of the second corner of the
1591 * destination rectangle.
1592 * @param dy2 the <i>y</i> coordinate of the second corner of the
1593 * destination rectangle.
1594 * @param sx1 the <i>x</i> coordinate of the first corner of the
1595 * source rectangle.
1596 * @param sy1 the <i>y</i> coordinate of the first corner of the
1597 * source rectangle.
1598 * @param sx2 the <i>x</i> coordinate of the second corner of the
1599 * source rectangle.
1600 * @param sy2 the <i>y</i> coordinate of the second corner of the
1601 * source rectangle.
1602 * @param observer object to be notified as more of the image is
1603 * scaled and converted.
1604 * @see java.awt.Image
1605 * @see java.awt.image.ImageObserver
1606 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1607 * @since JDK1.1
1608 */
1609 public boolean drawImage(Image img,
1610 int dx1, int dy1, int dx2, int dy2,
1611 int sx1, int sy1, int sx2, int sy2,
1612 ImageObserver observer) {
1613
1614 return drawImage(img,
1615 dx1, dy1, dx2, dy2,
1616 sx1, sy1, sx2, sy2,
1617 null, observer);
1618 }
1619
1620 /**
1621 * Draws as much of the specified area of the specified image as is
1622 * currently available, scaling it on the fly to fit inside the
1623 * specified area of the destination drawable surface.
1624 * <p>
1625 * Transparent pixels are drawn in the specified background color.
1626 * This operation is equivalent to filling a rectangle of the
1627 * width and height of the specified image with the given color and then
1628 * drawing the image on top of it, but possibly more efficient.
1629 * <p>
1630 * This method returns immediately in all cases, even if the
1631 * image area to be drawn has not yet been scaled, dithered, and converted
1632 * for the current output device.
1633 * If the current output representation is not yet complete then
1634 * <code>drawImage</code> returns <code>false</code>. As more of
1635 * the image becomes available, the process that draws the image notifies
1636 * the specified image observer.
1637 * <p>
1638 * This method always uses the unscaled version of the image
1639 * to render the scaled rectangle and performs the required
1640 * scaling on the fly. It does not use a cached, scaled version
1641 * of the image for this operation. Scaling of the image from source
1642 * to destination is performed such that the first coordinate
1643 * of the source rectangle is mapped to the first coordinate of
1644 * the destination rectangle, and the second source coordinate is
1645 * mapped to the second destination coordinate. The subimage is
1646 * scaled and flipped as needed to preserve those mappings.
1647 * @param img the specified image to be drawn
1648 * This method does nothing if <code>img</code> is null.
1649 * @param dx1 the <i>x</i> coordinate of the first corner of the
1650 * destination rectangle.
1651 * @param dy1 the <i>y</i> coordinate of the first corner of the
1652 * destination rectangle.
1653 * @param dx2 the <i>x</i> coordinate of the second corner of the
1654 * destination rectangle.
1655 * @param dy2 the <i>y</i> coordinate of the second corner of the
1656 * destination rectangle.
1657 * @param sx1 the <i>x</i> coordinate of the first corner of the
1658 * source rectangle.
1659 * @param sy1 the <i>y</i> coordinate of the first corner of the
1660 * source rectangle.
1661 * @param sx2 the <i>x</i> coordinate of the second corner of the
1662 * source rectangle.
1663 * @param sy2 the <i>y</i> coordinate of the second corner of the
1664 * source rectangle.
1665 * @param bgcolor the background color to paint under the
1666 * non-opaque portions of the image.
1667 * @param observer object to be notified as more of the image is
1668 * scaled and converted.
1669 * @see java.awt.Image
1670 * @see java.awt.image.ImageObserver
1671 * @see java.awt.image.ImageObserver#imageUpdate(java.awt.Image, int, int, int, int, int)
1672 * @since JDK1.1
1673 */
1674 public boolean drawImage(Image img,
1675 int dx1, int dy1, int dx2, int dy2,
1676 int sx1, int sy1, int sx2, int sy2,
1677 Color bgcolor,
1678 ImageObserver observer) {
1679
1680 if (img == null) {
1681 return true;
1682 }
1683 int imgWidth = img.getWidth(null);
1684 int imgHeight = img.getHeight(null);
1685
1686 if (imgWidth < 0 || imgHeight < 0) {
1687 return true;
1688 }
1689
1690 int srcWidth = sx2 - sx1;
1691 int srcHeight = sy2 - sy1;
1692
1693 /* Create a transform which describes the changes
1694 * from the source coordinates to the destination
1695 * coordinates. The scaling is determined by the
1696 * ratio of the two rectangles, while the translation
1697 * comes from the difference of their origins.
1698 */
1699 float scalex = (float) (dx2 - dx1) / srcWidth;
1700 float scaley = (float) (dy2 - dy1) / srcHeight;
1701 AffineTransform xForm
1702 = new AffineTransform(scalex,
1703 0,
1704 0,
1705 scaley,
1706 dx1 - (sx1 * scalex),
1707 dy1 - (sy1 * scaley));
1708
1709 /* drawImageToPlatform needs the top-left of the source area and
1710 * a positive width and height. The xform describes how to map
1711 * src->dest, so that information is not lost.
1712 */
1713 int tmp=0;
1714 if (sx2 < sx1) {
1715 tmp = sx1;
1716 sx1 = sx2;
1717 sx2 = tmp;
1718 }
1719 if (sy2 < sy1) {
1720 tmp = sy1;
1721 sy1 = sy2;
1722 sy2 = tmp;
1723 }
1724
1725 /* if src area is beyond the bounds of the image, we must clip it.
1726 * The transform is based on the specified area, not the clipped one.
1727 */
1728 if (sx1 < 0) {
1729 sx1 = 0;
1730 } else if (sx1 > imgWidth) { // empty srcArea, nothing to draw
1731 sx1 = imgWidth;
1732 }
1733 if (sx2 < 0) { // empty srcArea, nothing to draw
1734 sx2 = 0;
1735 } else if (sx2 > imgWidth) {
1736 sx2 = imgWidth;
1737 }
1738 if (sy1 < 0) {
1739 sy1 = 0;
1740 } else if (sy1 > imgHeight) { // empty srcArea
1741 sy1 = imgHeight;
1742 }
1743 if (sy2 < 0) { // empty srcArea
1744 sy2 = 0;
1745 } else if (sy2 > imgHeight) {
1746 sy2 = imgHeight;
1747 }
1748
1749 srcWidth = sx2 - sx1;
1750 srcHeight = sy2 - sy1;
1751
1752 if (srcWidth <= 0 || srcHeight <= 0) {
1753 return true;
1754 }
1755
1756 return drawImageToPlatform(img, xForm, bgcolor,
1757 sx1, sy1, srcWidth, srcHeight, false);
1758
1759
1760 }
1761
1762 /**
1763 * Draws an image, applying a transform from image space into user space
1764 * before drawing.
1765 * The transformation from user space into device space is done with
1766 * the current transform in the Graphics2D.
1767 * The given transformation is applied to the image before the
1768 * transform attribute in the Graphics2D state is applied.
1769 * The rendering attributes applied include the clip, transform,
1770 * and composite attributes. Note that the result is
1771 * undefined, if the given transform is noninvertible.
1772 * @param img The image to be drawn.
1773 * This method does nothing if <code>img</code> is null.
1774 * @param xform The transformation from image space into user space.
1775 * @param obs The image observer to be notified as more of the image
1776 * is converted.
1777 * @see #transform
1778 * @see #setTransform
1779 * @see #setComposite
1780 * @see #clip
1781 * @see #setClip
1782 */
1783 public boolean drawImage(Image img,
1784 AffineTransform xform,
1785 ImageObserver obs) {
1786
1787 if (img == null) {
1788 return true;
1789 }
1790
1791 boolean result;
1792 int srcWidth = img.getWidth(null);
1793 int srcHeight = img.getHeight(null);
1794
1795 if (srcWidth < 0 || srcHeight < 0) {
1796 result = false;
1797 } else {
1798 result = drawImageToPlatform(img, xform, null,
1799 0, 0, srcWidth, srcHeight, false);
1800 }
1801
1802 return result;
1803 }
1804
1805 /**
1806 * Draws a BufferedImage that is filtered with a BufferedImageOp.
1807 * The rendering attributes applied include the clip, transform
1808 * and composite attributes. This is equivalent to:
1809 * <pre>
1810 * img1 = op.filter(img, null);
1811 * drawImage(img1, new AffineTransform(1f,0f,0f,1f,x,y), null);
1812 * </pre>
1813 * @param op The filter to be applied to the image before drawing.
1814 * @param img The BufferedImage to be drawn.
1815 * This method does nothing if <code>img</code> is null.
1816 * @param x,y The location in user space where the image should be drawn.
1817 * @see #transform
1818 * @see #setTransform
1819 * @see #setComposite
1820 * @see #clip
1821 * @see #setClip
1822 */
1823 public void drawImage(BufferedImage img,
1824 BufferedImageOp op,
1825 int x,
1826 int y) {
1827
1828 if (img == null) {
1829 return;
1830 }
1831
1832 int srcWidth = img.getWidth(null);
1833 int srcHeight = img.getHeight(null);
1834
1835 if (op != null) {
1836 img = op.filter(img, null);
1837 }
1838 if (srcWidth <= 0 || srcHeight <= 0) {
1839 return;
1840 } else {
1841 AffineTransform xform = new AffineTransform(1f,0f,0f,1f,x,y);
1842 drawImageToPlatform(img, xform, null,
1843 0, 0, srcWidth, srcHeight, false);
1844 }
1845
1846 }
1847
1848 /**
1849 * Draws an image, applying a transform from image space into user space
1850 * before drawing.
1851 * The transformation from user space into device space is done with
1852 * the current transform in the Graphics2D.
1853 * The given transformation is applied to the image before the
1854 * transform attribute in the Graphics2D state is applied.
1855 * The rendering attributes applied include the clip, transform,
1856 * and composite attributes. Note that the result is
1857 * undefined, if the given transform is noninvertible.
1858 * @param img The image to be drawn.
1859 * This method does nothing if <code>img</code> is null.
1860 * @param xform The transformation from image space into user space.
1861 * @see #transform
1862 * @see #setTransform
1863 * @see #setComposite
1864 * @see #clip
1865 * @see #setClip
1866 */
1867 public void drawRenderedImage(RenderedImage img,
1868 AffineTransform xform) {
1869
1870 if (img == null) {
1871 return;
1872 }
1873
1874 BufferedImage bufferedImage = null;
1875 int srcWidth = img.getWidth();
1876 int srcHeight = img.getHeight();
1877
1878 if (srcWidth <= 0 || srcHeight <= 0) {
1879 return;
1880 }
1881
1882 if (img instanceof BufferedImage) {
1883 bufferedImage = (BufferedImage) img;
1884 } else {
1885 bufferedImage = new BufferedImage(srcWidth, srcHeight,
1886 BufferedImage.TYPE_INT_ARGB);
1887 Graphics2D imageGraphics = bufferedImage.createGraphics();
1888 imageGraphics.drawRenderedImage(img, xform);
1889 }
1890
1891 drawImageToPlatform(bufferedImage, xform, null,
1892 0, 0, srcWidth, srcHeight, false);
1893
1894 }
1895
1896 }